Building the basic ANN in MATLAB
In this programming exercise, we shall begin to build an ANN in the MATLAB Editor software environment. We shall breakdown the task into several steps, implementing some of the knowledge we have learned thus far, and later we shall return to the same build to extend the code further into new aspects of study. In this exercise we shall complete the following tasks:
- Devise and program a structure for storing the variable weight parameters and the general structure of the network
- Define the training pairs (known inputs + expected outputs) for an input pattern
- We shall then program a forward pass through the network for one pattern only and calculate the error
- We shall then program a backwards pass through the network to modify the weights
- We shall build the above code into a loop that allows us to train the network and create an error log on each loop.
- We shall plot the error values generated in each training loop to visualise the training effect.
In later exercises, we shall expand this build to:
- Integrate the capability to recognise multiple inputs into the network
- Devise and program how we want the ANN to stop training
In this exercise we shall be building the following network:
Figure 1: ANN for programming exercise 1
There are weights on every connection illustrated above, with the following notation: w_I2N2 is the weight between I2 and N2. Note that there shall not be a weight between the output of N3 & N4 and the output from the ANN (O1 & O2), e.g. O_N3 = O1. Each neuron shall be activated using the Sigmoid Activation Function.
The network shall be trained to recognise the following input pattern and output [O1 ; O2] = [1 ; 0] to represent identification.
Training Pair |
|||
I1 |
0 |
O1 |
1 |
I2 |
1 |
O2 |
0 |
Step 1: Setting up the structure of the network
- Open MATLAB R2020a
Note: Instructions for installing the application and setting up an account are included in UHI MatLab PDF - Open a new script and save the file in an appropriate location using a relevant filename.
Note: that we shall return to this file in future sections so be sure that the file is saved in a secure and accessible location. - Using the comment feature in the MATLAB programming language, write a descriptive introduction to your program that uniquely identifies this program, e.g.:
%{
...
Doug Rattray - doug.rattray@uhi.ac.uk
student ID - Lews Castle College, UHI
Date
ANN – Section 2 – Programming Exercise 1
...
%}
Define the structure of the ANN by initialising variables that contain the number of input, mid and output layer neurons.
Note: the number of weights in each layer is the product of the number of sending neurons with the number of receiving neurons.
% Structure of ANN
nInputs = 2;
nNeurMid = 2;
nNeurOut = 2;
nlayer1w = nInputs*nNeurMid;
nlayer2w = nNeurMid*nNeurOut;
Create an empty array (an array of zeros) for each neuron in which each row represents a separate neuron and three columns deep, where columns shall be used to contain the inputs to the neuron, outputs from the neuron and error values, e.g.:
Neuron |
Input |
Output |
Error |
N1 |
0 |
0 |
0 |
N2 |
0 |
0 |
0 |
Also, create two single column arrays to store the inputs to the ANN and the associated target outputs for our training pair.
% Array to Hold Neuron Variables
% 1:input | 2:output | 3:error
NeurMid = zeros(nNeurMid,3,"double");
NeurOut = zeros(nNeurOut,3,"double");
% Arrays to Hold training pairs
KnownIn = zeros(nInputs,1,"double");
TargetOut = zeros(nNeurOut,1,"double");
Create a single column array initialised with random values (between -1 and 1) for all weights that exist between each layer.
Note that we shall use the RAND function in MATLAB, which we will have to multiply by 2 and subtract 1 from to change its range from (0 < x ≤ 1) to (-1 < x ≤ 1).
weight |
value |
wI1N1 |
r |
wI2N1 |
r |
wI1N2 |
r |
wI2N2 |
r |
% arrays to store weight parameters
% Initial value: Random value: (-1 < value < +1)
layer1w = (rand(nlayer1w,1) * 2) - 1;
layer2w = (rand(nlayer2w,1) * 2) - 1;
State the number of training cycles (in this exercise: 5) that we want the ANN to process and the training rate (N).
% Training cycles and training rate (N)
nTCycles = 5;
N = 1;
Finally, create an array into which we shall store the error values calculated in each training cycle.
Note that the array should be sized such that the output from O1 & O2 can be stored for each training cycle.
ErrLOG |
O1 |
O2 |
|
TCycle |
1 |
- |
- |
2 |
- |
- |
|
3 |
- |
- |
% Array to store output error of each training cycle
ErrLog = zeros (nTCycles, nNeurOut, "double");
Step 2: Defining training pairs
In step 1, we created arrays called ‘KnownIn’ and ‘TargetOut’ in which to store our training pair values. We shall now populate these arrays with the training pair values stated in the problem statement:
Training pair |
|||
I1 |
0 |
O1 |
1 |
I2 |
1 |
O2 |
0 |
KnownIn = [0;1];
TargetOut = [1;0];
Step 3: Programming the forward pass
In this step, we will be seeking to take a number of steps to process the data flow through the ANN:
- Between the Input Layer and Mid (Hidden) Layer:
- Inputs shall be multiplied by the appropriate weights along connections.
- The values generated above shall be summed and stored as inputs into mid-layer neurons.
- The output from mid-layer neurons shall be processed through the sigmoid function.
- The above shall be repeated between the Mid Layer and the Output Layer
- By comparison with the Target Output, the error shall be calculated.
Note that in this process we shall use nested FOR loops to walk through the arrays of neurons and weights. Care should be taken to ensure that the correct weight is being multiplied by the correct neuron parameter. It may be useful to build an MS Excel Workbook to check that the code is being implemented correctly.
Several methods could be used to run through each weight. The method employed below follows the following process:
- take first receiving end neuron
- process each weighted connection that it receives one at a time
move onto next receiving end neuron and repeat until all receiving end neurons have been fully processed.
For each neuron in the Mid Layer, we shall calculate a rolling sum of the product of each input by its weight.
for m = 1:1:nNeurMid
for i = 1:1:nInputs
NeurMid(m,1) = NeurMid(m,1) + (KnownIn(i,1) *
layer1w(((m-1)*nInputs+i),1));
end
end
For each neuron in the Mid Layer, we shall calculate the output by passing its summed inputs through the sigmoid function.
for m = 1:1:nNeurMid
NeurMid(m,2) = 1 / (1 + exp(-NeurMid(m,1)));
end
We shall repeat the above two stages to process the data as it flows between the Mid Layer and Output Layer Neurons.
for o = 1:1:nNeurOut
for m = 1:1:nNeurMid
NeurOut(o,1) = NeurOut(o,1) + (NeurMid(m,2) *
layer2w(((o-1)*nNeurMid+m),1));
end
end
for o = 1:1:nNeurOut
NeurOut(o,2) = 1 / (1 + exp(-NeurOut(o,1)));
end
For each neuron in the Output Layer, we shall compare the actual output to the target output and calculate the error value.
- The error value shall be assigned to the 3rd column in the neuron array ‘NeurOut’.
Note: we must remember to implement the squashing function ‘’ as we are using the sigmoid activation.
for o = 1:1:nNeurOut
squash = NeurOut(o,2) * (1-NeurOut(o,2));
NeurOut(o,3) = squash * (TargetOut(o,1)-NeurOut(o,2));
end
Step 4: Programming the backward pass
In this step, we will be seeking to take a number of steps to process a backward pass through the network, i.e., we shall modify the weights of the network based on the error previously calculated.
- Prior to adjusting weights, we shall calculate the error in the Mid Layer (note that we already know the error in the Output Layer).
- Between the Output Layer and Mid Layer, we shall adjust each weight based on the error of the Output Layer
- Between the Mid Layer and Input Layer, we shall adjust each weight based on the error of the Mid Layer
Note that in this process we shall once again use nested FOR loops to walk through the arrays of neurons and weights. Again, care should be taken to ensure that the correct weight is being multiplied by the correct neuron parameter and students may find it useful to build an MS Excel Workbook to check that the code is being implemented correctly.
As was the case in the forward pass, several methods could be used to run through each weight. The method employed below follows the following process:
- take first sending end neuron
- process each weighted connection that it sends out one at a time
- move onto next sending end neuron and repeat until all sending end neurons have been fully processed.
This is the reverse of that done in the forward pass.
Calculate the Error for each Mid Layer Neuron
For each neuron in the Mid Layer, we shall calculate the error using a rolling sum of the product of each output layer error by its associated weight.
for m = 1:1:nNeurMid
for o = 1:1:nNeurOut
squashBP = NeurMid(m,2) * (1-NeurMid(m,2));
NeurMid(m,3) = NeurMid(m,3) + (NeurOut(o,3) *
layer2w(((m-1)*nNeurOut+o),1))*(squashBP);
end
end
Adjust the weights between the Mid Layer and the Output Layer
We shall now adjust the weights on connections between the Mid Layer and the Output Layer using the Back Propagation algorithm.
for o = 1:1:nNeurOut
for m = 1:1:nNeurMid
layer2w(((o-1)*nNeurMid+m),1) = layer2w(((o-1) *
nNeurMid+m),1) + N*(NeurOut(o,3) *
NeurMid(m,2));
end
end
Adjust the weights between the Input Layer and the Mid Layer
In the same manner, adjust the weights on connections between the Input Layer and the Mid Layer.
for m = 1:1:nNeurMid
for i = 1:1:nInputs
layer1w(((m-1)*nInputs+i),1) = layer1w(((m-1) *
nInputs+i),1) + N * (NeurMid(m,3) *
KnownIn(i,1));
end
end
Step 5: Building the training loop
In this step, we will design a simple loop around our Forward and Backward Passes so that we may observe our network being trained. At this stage, we will use a fixed number of training cycles, which we might later adapt to break when we include a decision making process in the code for when to stop training.
Designing the Training Cycle
The start point for our training cycle should be set just prior to our definition of the Training Pair (Step 2).
The end point for our training cycle should be after the error log is populated, but before the error is plotted.
for Tloop = 1:1:nTCycles
% ---------------------------------------------------%
% 2: Define Training Pair
% ---------------------------------------------------%
% ---------------------------------------------------%
% 3: Forward Pass
%- --------------------------------------------------%
% ---------------------------------------------------%
% 4: Backward Pass
%- --------------------------------------------------%
% Populating the Error Log
%----------------------------------------------------
end
Populating an Error Log
We calculated the output error at the end of each Forward Pass (Step 3) and assigned the value to the 3rd Column in the neuron array ‘NeurOut’. This value will be overwritten on each consecutive training cycle. As such, we should adapt that section of code to also send the code into our error log, as follows:
Note that this section must be encapsulated within the training loop, created previously.
for o = 1:1:nNeurOut
squash = NeurOut(o,2) * (1-NeurOut(o,2));
NeurOut(o,3) = squash * (TargetOut(o,1)-NeurOut(o,2));
ErrLog(Tloop, o) = abs(NeurOut(o,3));
end
Step 6: Plotting the error log
In this step, we shall simply plot the Error Log so that we may visually see how well the network has been trained.
hold on;
for o = 1:1:nNeurOut
plot(ErrLog(:,o));
end
title('Output error exposed to training');
ylabel('Error value');
xlabel('Number of training loops');
Figure 2: Example plot of ANN output error following training
Note: This error plot is only one example of how the ANN might behave and students should expect that their error plot will look somewhat different. Each time the program is run it is initiated with random variables thus the process of minimising error will follow a different route on each iteration. The important aspects to note are:
- On the first iteration, the error starts at a non-zero value.
- The error approaches zero after some period of training.